import moment from "moment";
import _ from "lodash";
import {
  MoveType
} from "../constants/constant";

/**
 *  根据日期获取日历第一天
 * @param {moment} date
 *
 */
function getCalendarStart(date) {
  if (!moment.isMoment(date) && !moment.isDate(date)) {
    throw Error("请传入Moment对象或Date对象");
  }
  date = moment(date).startOf("month");
  return moment(date).add(0 - date.day(), "d");
}

/**
 *  检验指定日期是否在日期范围之内
 * @param {moment} start
 * @param {moment} end
 * @param {moment} date
 */
function duringDatePeriod(start, end, date) {
  if (
    arguments.length !== 3 ||
    Array.from(arguments).some(arg => !moment.isMoment(arg)) ||
    start.valueOf() > end.valueOf()
  ) {
    throw Error("参数不合法");
  }

  const value = date.valueOf();
  return value >= start.valueOf() && value <= end.valueOf();
}

/**
 * 根据日期范围检验制定日期是否存在活动，0表示不存在，1表示存在，但不是开始和结束，
 * 2表示存在且为开始日，3表示存在且为结束日, 4表示存在，且活动在当天内
 * @param {moment} start
 * @param {moment} end
 * @param {moment} date
 */
function todayHasEvent(start, end, date) {
  if (
    arguments.length !== 3 ||
    Array.from(arguments).some(arg => !moment.isMoment(arg)) ||
    start.valueOf() > end.valueOf()
  ) {
    throw Error("参数不合法");
  }

  const dayStart = moment(date)
    .startOf("day")
    .valueOf();
  const dayEnd = moment(date)
    .endOf("day")
    .valueOf();
  let result = 0;
  if (start.valueOf() >= dayEnd || end.valueOf() <= dayStart) {
    result = 0;
  } else if (start.valueOf() < dayStart && end.valueOf() > dayEnd) {
    result = 1;
  } else if (start.valueOf() >= dayStart && end.valueOf() > dayEnd) {
    result = 2;
  } else if (start.valueOf() < dayStart && end.valueOf() <= dayEnd) {
    result = 3;
  } else if (start.valueOf() >= dayStart && end.valueOf() <= dayEnd) {
    result = 4;
  }
  return result;
}

/**
 *
 * @param {array} events
 * @param {object} date
 * @param {Map} map
 */
function sortAndSupplementEvent(events, date, map) {
  if (!Array.isArray(events) || date === undefined || !_.isMap(map)) {
    throw Error("参数不合法");
  }
  if (!events.length) {
    return;
  }
  let result = [];
  let copyEvents = Array.from(events);
  if (map.size) {
    for (let [key, value] of map) {
      const eventIndex = _.findIndex(copyEvents, {
        id: key
      });
      if (eventIndex >= 0) {
        const event = copyEvents.splice(eventIndex, 1)[0];
        result[value] = event;
        if (event.isEnd) {
          map.delete(event.id);
        }
      }
    }
  }

  // 排序
  copyEvents = sortEvents(copyEvents);

  let eventIndex = 0;
  copyEvents.forEach((event, index) => {
    while (true) {
      if (!result[eventIndex]) {
        !event.isEnd && map.set(event.id, eventIndex);
        result[eventIndex++] = event;
        break;
      }
      eventIndex++;
    }
  });

  result = trimRightEmptyAndSupplementNull(result);
  return result;
}

function sortEvents(events) {
  if (!Array.isArray(events)) {
    throw Error("请传入event对象数组");
  }

  return Array.from(events).sort((event1, event2) => {
    if (event1.isStart && event2.isStart) {
      return event2.end.valueOf() - event1.end.valueOf();
    } else if (!event1.isStart && event2.isStart) {
      return -1;
    } else if (event1.isStart && !event2.isStart) {
      return 1;
    } else if (!event1.isEnd && !event2.isEnd) {
      return (
        event1.begin.valueOf() - event2.begin.valueOf() ||
        event2.end.valueOf() - event1.end.valueOf()
      );
    } else if (!event1.isEnd && event2.isEnd) {
      return -1;
    } else if (event1.isEnd && !event2.isEnd) {
      return 1;
    } else {
      return event1.begin.valueOf() - event2.begin.valueOf();
    }
  });
}

/**
 * trim掉数组最后的空选项，然后以null替换中间的空选项
 * @param {array} array
 */
function trimRightEmptyAndSupplementNull(array) {
  if (!Array.isArray(array)) {
    throw Error("参数不合法");
  }

  let isEnd = true;

  for (let i = Array.from(array).length - 1; i >= 0; i--) {
    if (!array[i]) {
      if (isEnd) {
        array.splice(i, 1);
      } else {
        array[i] = null;
      }
    } else if (isEnd) {
      isEnd = false;
    }
  }
  return array;
}

/**
 *
 * @param {string} hex
 * @param {number} opacity
 */
function cssHexToRGBA(hex, opacity) {
  if (!_.isString(hex) || !hex.includes("#") || !_.isNumber(opacity)) {
    throw Error("参数不合法");
  }
  hex = hex.substr(1);

  // 简写补全
  if (hex.length === 3) {
    hex = hex
      .split("")
      .map(str => `${str}${str}`)
      .join("");
  }
  const red = parseInt(parseInt(hex.substr(0, 2), 16), 10);
  const green = parseInt(parseInt(hex.substr(2, 2), 16), 10);
  const blue = parseInt(parseInt(hex.substr(4), 16), 10);

  return `rgba(${red}, ${green}, ${blue}, ${opacity})`;
}

/**
 * 获取随机16进制颜色值
 */
function getRandomHexColor() {
  let result = "#";
  const color = Array.from(new Array(6))
    .map((index, item) => {
      return Math.floor(Math.random() * 16).toString(16);
    })
    .join("");
  return result + color;
}

/**
 * 根据拖动方式、日期计算最终的开始日期和结束日期
 * @param {object} moveOption
 * @param {object} event
 * @param {moment} date 截至日期
 */
function calculateDates(moveOption, event, date) {
  if (!moveOption || !moveOption.moveType || !event || !date) {
    throw Error("参数非法");
  }

  if (date.valueOf() === moveOption.originDate.valueOf()) {
    return {
      moveOption,
      start: event.begin,
      end: event.end
    };
  }

  switch (moveOption.moveType) {
    case MoveType.NEW_EVENT:
      return calculateNewDates(event, moveOption, date);
    case MoveType.MOVE_EVENT:
      return calculateMoveDates(event, moveOption, date);
    case MoveType.STRETCH_EVENT:
      return calculateStretchDates(event, moveOption, date);
    default:
      return {
        moveOption,
        start: event.begin,
        end: event.end
      };
  }
}

/**
 * 计算新建事件的开始、结束日期
 * @param {*} event
 * @param {moment} date
 */
function calculateNewDates(event, moveOption, date) {
  let option = Object.assign({}, moveOption);
  let start = moment(event.begin);
  let end = moment(event.end);
  const {
    originDate
  } = moveOption;
  option.originDate = date;
  const diff = date.diff(originDate, "days");
  if (date.valueOf() >= event.begin.valueOf()) {
    end.add(diff, "days");
  } else {
    start.add(diff, "days");
  }
  return {
    moveOption: option,
    start,
    end
  };
}

/**
 * 计算整体移动事件的开始、结束日期
 * @param {*} event
 * @param {number} date
 */
function calculateMoveDates(event, moveOption, date) {
  const {
    originDate
  } = moveOption;
  const diff = date.diff(originDate, "days");
  let option = Object.assign({}, moveOption, {
    originDate: date,
  });
  return {
    moveOption: option,
    start: moment(event.begin).add(diff, "days"),
    end: moment(event.end).add(diff, "days")
  };
}

/**
 * 计算拉伸事件的开始、结束日期
 * @param {string} direction 拉伸方向
 * @param {moment} originDate
 * @param {*} event
 * @param {moment} date
 */
function calculateStretchDates(event, moveOption, date) {
  let option = Object.assign({}, moveOption);
  let start = moment(event.begin);
  let end = moment(event.end);
  const {
    originDate,
    direction
  } = moveOption;
  if (date.valueOf() === originDate.valueOf()) {
    return {
      moveOption,
      start: event.begin,
      end: event.end
    };
  }

  if (direction === "left") {
    if (date.valueOf() <= event.end.valueOf()) {
      const diff = date.diff(originDate, "days");
      start.add(diff, "days");
    } else {
      const diff = date.diff(moment(event.end).startOf("day"), "days");
      end.add(diff, "days");
      option.direction = "right";
    }
  } else {
    if (date.valueOf() >= event.begin.valueOf()) {
      const diff = date.diff(originDate, "days");
      end.add(diff, "days");
    } else {
      const diff = date.diff(moment(event.begin).startOf("day"), "days");
      start.add(diff, "days");
      option.direction = "left";
    }
  }
  option.originDate = date;
  return {
    moveOption: option,
    start,
    end
  };
}


function sourceCanDrag(canDrag, moveType, date) {
  if (_.isBoolean(canDrag)) {
    return canDrag;
  }
  if (_.isObject(canDrag)) {
    let dragOption = canDrag;
    if (_.isFunction(canDrag)) {
      dragOption = canDrag(date);
    }
    switch(moveType){
      case MoveType.NEW_EVENT:
        return dragOption.new;
      case MoveType.MOVE_EVENT:
        return dragOption.move;
      case MoveType.STRETCH_EVENT:
        return dragOption.stretch;
    }
  }
  return true;
}

export {
  getCalendarStart,
  duringDatePeriod,
  todayHasEvent,
  sortAndSupplementEvent,
  cssHexToRGBA,
  getRandomHexColor,
  calculateDates,
  sourceCanDrag,
};